import { pluginNodePolyfill, type PluginNodePolyfillOptions } from '@rsbuild/plugin-node-polyfill';
import { pluginBabel, type BabelConfigUtils } from '@rsbuild/plugin-babel';

import { isProd, helper, deepmerge, type RsbuildConfig } from './rsbuild.base.config';
const path = require("path");
const fs = require("fs");

interface GetBabelConfigParams {
  styledJsxEnabled?: boolean,
  antdStyleEnabled?: boolean,
  modifyBabelConfig?: (babelConfigUtils: BabelConfigUtils) => void,
}

/**
 * 参数文档配置: https://rsbuild.dev/zh/plugins/list/plugin-babel
 * 获取babel配置
 */
export const getBabelConfig = (params: GetBabelConfigParams = {}): RsbuildConfig => {
  const { styledJsxEnabled = true, antdStyleEnabled = true, modifyBabelConfig } = params;

  return {
    plugins: [
      // babel插件
      pluginBabel({
        babelLoaderOptions: (config, babelConfigUtils) => {
          const { addPlugins } = babelConfigUtils;
          // 添加styled-jsx的使用
          if (styledJsxEnabled) addPlugins([
            [
              "styled-jsx/babel",
              {
                "vendorPrefixes": true,     // 为css自动添加前缀
                "optimizeForSpeed": isProd, // 是否优化
                "plugins": [
                  ["styled-jsx-plugin-sass", { sassOptions: {} }]
                ]
              }
            ]
          ]);

          // 开发模式下开启antd-style，方便调试
          if (antdStyleEnabled && !isProd) addPlugins(["antd-style"])

          if (modifyBabelConfig) modifyBabelConfig(babelConfigUtils)
        },
      }),
    ]
  }
}

interface GetNodePolyfillParams {
  nodePolyfillOptions?: PluginNodePolyfillOptions,
  cesiumEngine?: {
    enable?: boolean,   // 是否开启cesium配置
    options: {
      srcDirPath: string,   // cesium engine的基础目录路基
      distDirPath: string,  // 目标路径
      modifyCodeEnable?: boolean,  // 是否允许修改源代码
      codeFindContent?: string | RegExp,  // 查找内容
      codeReplaceContent?: string,  // 替换内容
    },
    extraDisposeFn?: (config: RsbuildConfig) => RsbuildConfig,
  }
}

/**
 * 配置node-polyfill
 */
export const getNodePolyfillConfig = ({ nodePolyfillOptions = {}, cesiumEngine }: GetNodePolyfillParams): RsbuildConfig => {
  let config: RsbuildConfig = {};

  // 配置cesiumEngine
  if (cesiumEngine) {
    const { enable = true, options, extraDisposeFn } = cesiumEngine;

    if (enable) {
      const { srcDirPath, distDirPath, modifyCodeEnable = true, codeFindContent, codeReplaceContent } = options;
      config.output = {
        copy: [
          // { from: path.join(srcDirPath, 'Build/Workers'), to: distDirPath+'/Workers' },
          { from: path.join(srcDirPath, 'Source/Assets'), to: distDirPath + '/Assets' },
          { from: path.join(srcDirPath, 'Build/ThirdParty'), to: distDirPath + '/ThirdParty' },
        ],
      }

      config.tools = {
        rspack: {
          // 注意: rsbuild编译代码时import.meta不支持，暂时将import.meta替换成'{}'空对象
          // plugins: [
          //   new rspack.DefinePlugin({
          //     'import.meta': '{}'
          //   })
          // ],
          module: {
            rules: [
              // 添加.glsl文件转为字符串
              {
                test: /\.glsl$/,
                type: 'asset/source',
              },
            ]
          }
        }
      }

      nodePolyfillOptions = deepmerge(nodePolyfillOptions, {
        globals: {
          https: false,
          http: false,
          zlib: false,
          url: false,
          buffer: false,
          assert: false,
          util: false,
          stream: false,
        }
      })


      if (modifyCodeEnable) {
        //***********************************修改源码start****************************************
        /*
          //d frontToBack添加try catch的原因, 调用Cesium.DrawCommand并传入Cesium.PASS.TRANSLUCENT(半透明物体)时，在监听点击或鼠标移动事件等时, 会报错: TypeError: Cannot read properties of undefined (reading 'distanceSquaredTo')
          // 这样会影响程序的运行，所以暂时添加try catch来抑制异常的抛出
          new Cesium.DrawCommand({
            ...
            ...
            pass: Cesium.PASS.TRANSLUCENT,
          });
        */
        const fs = require('fs');
        const srcFilePath = `${srcDirPath}/Source/Scene/Scene.js`;
        const data = fs.readFileSync(srcFilePath, 'utf-8');

        // 匹配函数体内是否添加了try catch
        const functionRegex = /function\s+frontToBack\s*\(([^)]*)\)\s*{([^}]*)}/;
        const tryCatchRegex = /\btry\b[\s\S]*?/;
        const match = data.match(functionRegex);
        if (match) {
          const functionBody = match[2]; // 获取函数体内容

          // 未添加try catch则添加
          if (!tryCatchRegex.test(functionBody)) {
            let findStr: string | RegExp = /function\s+frontToBack\s*\(([^)]*)\)\s*{([^}]*)}/
            if (codeFindContent) findStr = codeFindContent;

            let replacedStr = `function frontToBack(a, b, position) {
  // When distances are equal equal favor sorting b before a. This gives render priority to commands later in the list.
  try{
    return (
      a.boundingVolume.distanceSquaredTo(position) -
      b.boundingVolume.distanceSquaredTo(position) +
      CesiumMath.EPSILON12
    );
  }catch(e){

  }
}`
            if (codeReplaceContent) replacedStr = codeReplaceContent;
            const replaced = data.replace(findStr, replacedStr);
            fs.writeFileSync(srcFilePath, replaced, 'utf-8')
          }
        }
      }


      // TODO: 开发环境下copy Build/Workers正常，生成环境下打包copy Build/Workers会报如下错误:
      //   × JavaScript parsing error: 'import', and 'export' cannot be used outside of module code
      //     ╭─[24:1]
      //  24 │  */
      //  25 │ 
      //  26 │ import {
      //     · ──────
      //  27 │   defined_default
      //  28 │ } from "./chunk-YBKFS53K.js";
      //     ╰────
      // 所以在生成环境下打包不器用copy Build/Workers, 而是采用打包换成后再将Build/Workers放到指定目录中, 
      // copy过程暂时发到package.json scripts build命令中了，请参照

      if (!isProd) {
        if (config.output?.copy) {
          (config.output?.copy as any[]).push({
            from: path.join(srcDirPath, 'Build/Workers'), to: distDirPath + '/Workers'
          })
        }
      } else {
        setTimeout(() => {
          const dist = path.join(path.resolve('./'), "dist/" + distDirPath + "/Workers");
          if (!fs.existsSync(dist)) fs.mkdirSync(dist, { recursive: true });
          helper.copy(path.join(srcDirPath, 'Build/Workers'), dist)
        }, 3000)
      }

      // 额外处理函数
      if (extraDisposeFn) config = extraDisposeFn(config)
    }
  }

  // 添加node-polyfill插件
  config.plugins = [pluginNodePolyfill(nodePolyfillOptions)]

  return config;
}
